//
//
//
//
//
// 启动鼠标事件
import { first, get, isEmpty } from "lodash";
import { Camera, Intersection, Object3D, Raycaster, Renderer, Scene, Vector2, WebGLRenderer } from "three";

export interface THREEMouseEvent extends Intersection {
    renderer: Renderer;
    scene: Scene;
    camera: Camera;
    type: string;
    offsetX: number;
    offsetY: number
    x: number;
    y: number;
    object: any;
}

// three 的鼠标事件
export default class THREEMouse {

    constructor(renderer: WebGLRenderer, camera: Camera, scene: Scene) {

        this._renderer = renderer;

        this._frame = renderer.info.render.frame;

        this._camera = camera;

        this._scene = scene;

        this._renderer.domElement.addEventListener("mouseenter", () => { this._mouseInRenderer = true }, true); //鼠标进入开启拾取
        this._renderer.domElement.addEventListener("mouseleave", this._mouseleave, true); //鼠标离开停止拾取

        this._renderer.domElement.addEventListener("mousemove", this._mousemove, true); //鼠标移动监听事件
        this._renderer.domElement.addEventListener("pointerdown", this._mousedown, true); //鼠标按下监听事件
        this._renderer.domElement.addEventListener("pointerup", this._mouseup, true); //鼠标弹起监听事件
    }

    // 渲染器
    _renderer!: WebGLRenderer;

    // 相机对象
    _camera!: Camera;

    // 场景对象
    _scene!: Scene;

    // 鼠标在渲染器上面的位置
    _mosePosition = new Vector2(0, 0);

    // 鼠标按下的位置 如果和鼠标弹起的位置相同 才算一次有效的点击事件
    _mouseDownPosition = new Vector2(0, 0);

    //当前鼠标指针上的对象
    _mesh_on?: any;

    //鼠标按下的对象
    _mesh_Down?: any;

    //射线拾取器
    _raycaster = new Raycaster();

    // 当前渲染的帧数 ( 鼠标滑动事件 一帧只需要执行费一次即可 )
    _frame: number = 0;

    //鼠标是否在渲染器上
    _mouseInRenderer: boolean = false;

    // 鼠标滑动的事件
    _mousemoveEvent?: MouseEvent;

    // 需要被拾取的对象数组
    _object3ds: Object3D[] = [];

    setCamera(camera: Camera) {
        this._camera = camera;
    }

    // 获取拾取到的对象
    _getSelected(object3ds: Object3D[]): Intersection | undefined {
        let intersection: Intersection | undefined;

        if (!isEmpty(object3ds)) {
            intersection = first(this._raycaster.intersectObjects(object3ds, false));
        }

        return intersection;
    };

    // 分发鼠标拾取事件
    dispatchEvent(object3ds: Object3D[]) {

        // 没有鼠标事件就不往下执行了
        if (!this._mousemoveEvent || !this._mouseInRenderer) {
            return;
        }

        this._object3ds = object3ds;

        // 拾取模型
        let intersection = this._getSelected(object3ds);

        if (intersection) {

            const MouseEvent: THREEMouseEvent = Object.assign(intersection, {  // 将来可能用到的对象
                renderer: this._renderer,
                scene: this._scene,
                camera: this._camera,
                object: intersection.object as any,
                type: this._mousemoveEvent.type,
                offsetX: this._mousemoveEvent.offsetX,
                offsetY: this._mousemoveEvent.offsetY,
                x: this._mousemoveEvent.x,
                y: this._mousemoveEvent.y
            });

            // 触发滑动事件
            if (this._mesh_on === MouseEvent.object) {
                if (MouseEvent.object.onMousemove instanceof Function) {
                    MouseEvent.type = "mousemove";
                    MouseEvent.object.onMousemove(MouseEvent);
                }
            }

            // 鼠标划入滑出事件
            if (this._mesh_on !== MouseEvent.object) {

                if (this._mesh_on && this._mesh_on.onMouseleave instanceof Function) {
                    MouseEvent.type = "mouseleave";

                    if (this._mesh_on && this._mesh_on.onMouseleave) {
                        this._mesh_on.onMouseleave(MouseEvent);
                    }
                }

                if (get(MouseEvent, ["object", "onMouseenter"]) instanceof Function) {
                    MouseEvent.type = "mouseenter";

                    if (MouseEvent.object.onMouseenter) {
                        MouseEvent.object.onMouseenter(MouseEvent);
                    }
                }
            }

            this._mesh_on = MouseEvent.object || undefined; // 设置为当前鼠标上的对象

            this._renderer.domElement.style.cursor = get(this, ["_mesh_on", "cursor"]) || "auto"; // 鼠标样式

        } else {

            // 鼠标离开事件
            if (this._mesh_on && this._mesh_on.onMouseleave) {
                //@ts-ignore
                this._mesh_on.onMouseleave({
                    renderer: this._renderer,
                    scene: this._scene,
                    camera: this._camera,
                    object: this._mesh_on as Object3D,
                    type: "mouseleave",
                    offsetX: this._mousemoveEvent.offsetX,
                    offsetY: this._mousemoveEvent.offsetY,
                    x: this._mousemoveEvent.x,
                    y: this._mousemoveEvent.y
                });
            }

            // 置空当前鼠标上面的对象
            this._mesh_on = undefined;

            this._renderer.domElement.style.cursor = "auto"; // 鼠标样式
        }


    }

    // 鼠标离开事件
    _mouseleave = (e: MouseEvent) => {
        this._mouseInRenderer = false;

        // 鼠标离开事件
        if (this._mousemoveEvent && this._mesh_on && this._mesh_on.onMouseleave) {
            //@ts-ignore
            this._mesh_on.onMouseleave({
                renderer: this._renderer,
                scene: this._scene,
                camera: this._camera,
                object: this._mesh_on as Object3D,
                type: "mouseleave",
                offsetX: this._mousemoveEvent.offsetX,
                offsetY: this._mousemoveEvent.offsetY,
                x: this._mousemoveEvent.x,
                y: this._mousemoveEvent.y
            });
        }

        // 置空当前鼠标上面的对象
        this._mesh_on = undefined;

        this._renderer.domElement.style.cursor = "auto"; // 鼠标样式
    }

    // 鼠标滑动 设置鼠标位置
    _mousemove = (e: MouseEvent) => {
        e.preventDefault();

        this._mousemoveEvent = e;

        if (this._frame !== this._renderer.info.render.frame) { // 一帧只执行一次即可

            this._frame = this._renderer.info.render.frame;

            if (document.pointerLockElement) { // 如果当前有元素 锁住了鼠标

                this._mosePosition.set(0, 0); // 鼠标就指向canvas 的正中心
            } else {

                this._mosePosition.x = (e.offsetX / this._renderer.domElement.offsetWidth) * 2 - 1;
                this._mosePosition.y = -(e.offsetY / this._renderer.domElement.offsetHeight) * 2 + 1;

                // 记录按下的位置
                this._mouseDownPosition.set(e.offsetX, e.offsetY);
            }
            this._raycaster.setFromCamera(this._mosePosition, this._camera); // 设置射线拾取的参数

        }
    };

    // 鼠标按下事件
    _mousedown = (e: MouseEvent) => {

        this._mousemove(e);

        let intersection = this._getSelected(this._object3ds);

        if (!intersection || !this._mousemoveEvent) {
            return
        }

        this._mesh_Down = this._mesh_on = intersection.object as Object3D;

        // 触发鼠标按下事件
        if (this._mesh_Down && this._mesh_Down.onMousedown instanceof Function) {

            const MouseEvent: THREEMouseEvent = Object.assign(intersection, {  // 将来可能用到的对象
                renderer: this._renderer,
                scene: this._scene,
                camera: this._camera,
                object: intersection.object as Object3D,
                type: "mousedown",
                offsetX: this._mousemoveEvent.offsetX,
                offsetY: this._mousemoveEvent.offsetY,
                x: this._mousemoveEvent.x,
                y: this._mousemoveEvent.y
            });

            if (this._mesh_Down.onMousedown) {
                this._mesh_Down.onMousedown(MouseEvent);
            }
        }

    }

    // 鼠标弹起事件
    _mouseup = (e: PointerEvent) => {

        let intersection = this._getSelected(this._object3ds);

        if (!intersection || !this._mousemoveEvent) {
            return;
        }

        this._mesh_on = intersection.object as Object3D;

        if (this._mesh_on && this._mesh_on.onMouseup instanceof Function) {

            const MouseEvent: THREEMouseEvent = Object.assign(intersection, {  // 将来可能用到的对象
                renderer: this._renderer,
                scene: this._scene,
                camera: this._camera,
                object: intersection.object as Object3D,
                type: "mouseup",
                offsetX: this._mousemoveEvent.offsetX,
                offsetY: this._mousemoveEvent.offsetY,
                x: this._mousemoveEvent.x,
                y: this._mousemoveEvent.y
            });

            if (this._mesh_on.onMouseup) {
                this._mesh_on.onMouseup(MouseEvent);
            }
        }

        // 触发鼠标弹起事件
        if (
            this._mesh_Down &&
            this._mesh_Down === this._mesh_on &&
            Math.abs(this._mouseDownPosition.x - e.offsetX) < 5 &&
            Math.abs(this._mouseDownPosition.y - e.offsetY) < 5 &&
            this._mesh_Down.onClick instanceof Function
        ) {

            const MouseEvent: THREEMouseEvent = Object.assign(intersection, {  // 将来可能用到的对象
                renderer: this._renderer,
                scene: this._scene,
                camera: this._camera,
                object: intersection.object as Object3D,
                type: "click",
                offsetX: this._mousemoveEvent.offsetX,
                offsetY: this._mousemoveEvent.offsetY,
                x: this._mousemoveEvent.x,
                y: this._mousemoveEvent.y
            });

            if (this._mesh_Down.onClick) {
                this._mesh_Down.onClick(MouseEvent);
            }
        }
    }

}